﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FireZone.NativeWMDMInteface;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;

namespace FireZone.WMDMHelper
{
    internal class WMDMOperationImplHelper : IWMDMOperation3
    {
        private string _fileName;
        private FileStream _fileStream;
        private FileAccess? _fileAccess;
        private CancellationToken _cancellationToken;
        private bool _createAsEmpty;
        
        public WMDMOperationImplHelper(string fileName, CancellationToken cancellationToken, bool createAsEmpty)
        {
            _fileName = fileName;
            _cancellationToken = cancellationToken;
            _createAsEmpty = createAsEmpty;
        }       


        #region IWMDMOperation3 Members

        public void BeginRead()
        {
            _fileStream = new FileStream(_fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
            _fileAccess = FileAccess.Read;
        }

        public void BeginWrite()
        {
            if(!_createAsEmpty)
                _fileStream = new FileStream(_fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            _fileAccess = FileAccess.Write;
        }

        public void GetObjectName(StringBuilder pwszName, int nMaxChars)
        {
            pwszName.Append(Path.GetFileName(_fileName), 0, Math.Min(nMaxChars, Path.GetFileName(_fileName).Length));
        }

        public void SetObjectName(string pwszName, int nMaxChars)
        {
            throw new NotImplementedException();
        }

        public void GetObjectAttributes(out WMDM_STORAGE_ATTR pdwAttributes, /*out _WAVEFORMATEX*/ IntPtr pFormat)
        {
            //throw new NotImplementedException();
            pdwAttributes = WMDM_STORAGE_ATTR.WMDM_FILE_ATTR_FILE;            
        }

        public void SetObjectAttributes(WMDM_STORAGE_ATTR dwAttributes, /*ref _WAVEFORMATEX*/ IntPtr pFormat)
        {
            throw new NotImplementedException();
        }

        public void GetObjectTotalSize(out uint pdwSizeLow, out uint pdwSizeHigh)
        {
            if (_createAsEmpty)
            {
                pdwSizeLow = pdwSizeHigh = 0;
            }
            else
            {
                pdwSizeLow = (uint)_fileStream.Length;
                pdwSizeHigh = (uint)(_fileStream.Length >> 32);
            }
        }

        public void SetObjectTotalSize(uint dwSizeLow, uint dwSizeHigh)
        {
            _fileStream.SetLength((long)dwSizeLow + ((long)dwSizeHigh << 32));
        }

        public int TransferObjectData(byte[] pData, ref int pdwSize, byte[] abMac)
        {
            int iRet = 0;
            if (_cancellationToken.IsCancellationRequested) return unchecked((int)WMDM_ERROR.WMDM_E_USER_CANCELLED);
            if(_fileAccess == FileAccess.Read)
            {
                byte[] tempbuf = new byte[pdwSize];
                Array.Copy(pData, tempbuf, pdwSize);
                if((iRet = NativeMethod.MTPDecryptParam(tempbuf, ref pdwSize, abMac)) >= 0)
                {
                    _fileStream.Write(tempbuf, 0, pdwSize);
                }
            }
            else if(_fileAccess == FileAccess.Write)
            {
                pdwSize = _fileStream.Read(pData, 0, pdwSize);
                if (pdwSize == 0) return 1;
                iRet = NativeMethod.MTPEncryptParam(pData, ref pdwSize, abMac);
            }
            return iRet;
        }

        public void End(ref uint phCompletionCode, object pNewObject)
        {
            Interlocked.Exchange<FileStream>(ref _fileStream, null).SafeCloseStream();
            _fileAccess = null;
        }

        public int TransferObjectDataOnClearChannel(byte[] pData, ref int pdwSize)
        {
            if (_cancellationToken.IsCancellationRequested) return unchecked((int)WMDM_ERROR.WMDM_E_USER_CANCELLED);
            //throw new NotImplementedException();            
            if(_fileAccess == FileAccess.Read)
            {
                _fileStream.Write(pData, 0, pdwSize);
            }
            else if(_fileAccess == FileAccess.Write)
            {
                pdwSize = _fileStream.Read(pData, 0, pdwSize);
                if (pdwSize == 0)
                {
                    return 1;
                }
            }
            return 0;
        }
        #endregion
    }
}
